﻿using System;
using System.Collections.Generic;
using System.Text;

namespace Vux.Helper.Numeric
{
    public static class ByteHelper
    {
        /// <summary>
        /// Ghép nối mảng dữ liệu kiểu byte
        /// </summary>
        public static void Concat(ref byte[] ip_destination, byte[] ip_source)
        {
            if (ip_source == null)
                return;
            var v_index_ = ip_destination.Length;
            Array.Resize(ref ip_destination, ip_destination.Length + ip_source.Length);
            Array.Copy(ip_source, 0, ip_destination, v_index_, ip_source.Length);
        }
        /// <summary>
        /// Ghép nối mảng dữ liệu kiểu byte
        /// </summary>
        public static void Concat(ref byte[] ip_destination, byte[] ip_source, int offset, int len)
        {
            if (ip_source == null)
                return;
            var v_index_ = ip_destination.Length;
            Array.Resize(ref ip_destination, ip_destination.Length + len);
            Array.Copy(ip_source, offset, ip_destination, v_index_, len);
        }
        /// <summary>
        /// Ghép nối mảng dữ liệu kiểu byte
        /// </summary>
        public static void Concat(ref byte[] ip_destination, byte ip_byte)
        {
            var v_dlength = ip_destination.Length;
            Array.Resize<byte>(ref ip_destination, v_dlength + 1);
            ip_destination[v_dlength] = ip_byte;
        }
        /// <summary>
        /// Ghép nối mảng dữ liệu kiểu byte
        /// </summary>
        public static void Concat(ref byte[] ip_destination, string ip_source)
        {
            var buffer = Encoding.UTF8.GetBytes(ip_source);
            Concat(ref ip_destination, buffer);
        }
        /// <summary>
        /// Ghép nối mảng dữ liệu kiểu byte
        /// </summary>
        public static void Concat(ref byte[] ip_destination, string ip_source, Encoding encoding)
        {
            var buffer = encoding.GetBytes(ip_source);
            Concat(ref ip_destination, buffer);
        }
        /// <summary>
        /// Trích xuất dữ liệu phần tử của mảng
        /// </summary>
        public static byte GetByte(this byte[] source, int index)
        {
            return source[index];
        }
        /// <summary>
        /// Trích xuất mảng con
        /// </summary>
        public static byte[] GetBytes(this byte[] source, int lowerindex, int length)
        {
            var buffer = new byte[length];
            Array.Copy(source, lowerindex, buffer, 0, length);
            return buffer;
        }
        /// <summary>
        /// Trích xuất mảng con
        /// </summary>
        public static byte[] GetBytes(this byte[] source, int index)
        {
            var buffer = new byte[0];
            for (int i = index; i < source.Length; i++)
            {
                Concat(ref buffer, source[i]);
            }
            return buffer;
        }
        /// <summary>
        /// Đổi dữ liệu kiểu byte sang số nguyên 32 bit
        /// </summary>
        public static uint ToUIntValue(this byte[] buffer, int startIndex)
        {
            return BitConverter.ToUInt32(buffer, startIndex);
        }
        /// <summary>
        /// Đổi dữ liệu kiểu byte sang số nguyên 32 bit
        /// </summary>
        public static int ToIntValue(this byte[] buffer, int startIndex)
        {
            if (buffer == null)
            {
                throw new ArgumentNullException("buffer");
            }
            var result = 0;
            for (int i = 0; i < 4 && (startIndex + i < buffer.Length); i++)
            {
                result |= buffer[i] << (8 * i);
            }
            return result;
        }
        /// <summary>
        /// Đổi dữ liệu sang lưu trữ mảng
        /// </summary>
        public static byte[] ToBytes(this ushort value)
        {
            return BitConverter.GetBytes(value);
        }
        /// <summary>
        /// Đổi dữ liệu sang lưu trữ mảng
        /// </summary>
        public static byte[] ToBytes(this uint value)
        {
            return BitConverter.GetBytes(value);
        }
        /// <summary>
        /// Đổi dữ liệu sang lưu trữ mảng
        /// </summary>
        public static byte[] ToBytes(this int value)
        {
            return BitConverter.GetBytes(value);
        }
        #region Xử lý số lớn
        /// <summary>
        /// Nhân mảng giá trị với một số
        /// </summary>
        /// <param name="ip_byte_source"></param>
        /// <param name="ip_int_value"></param>
        /// <returns></returns>
        public static byte[] Mutiply(this byte[] ip_byte_source, int ip_int_value)
        {
            var v_len = ip_byte_source.Length;
            var v_buffer = new byte[v_len];   // mảng tích kết quả
            var v_carry = 0;    //Cờ nhớ
            for (int i = 0; i < v_len; i++)
            {
                var v_composition = ip_byte_source[i] * ip_int_value + v_carry;
                v_buffer[i] = (byte)v_composition;  // mod 256
                v_carry = v_composition >> 8;   // div 256
            }
            if (v_carry > 0)
            {
                // mở rộng mảng kết quả v_buffer để lưu thêm 1 phần tử nhớ
                var buffer = new byte[1] { (byte)v_carry };
                Array.Resize(ref v_buffer, v_len + 1);
                Array.Copy(buffer, 0, v_buffer, v_len, 1);
            }
            return v_buffer;
        }
        /// <summary>
        /// Nhân mảng giá trị với một số
        /// </summary>
        /// <param name="ip_byte_source"></param>
        /// <param name="ip_int_value"></param>
        /// <returns></returns>
        public static byte[] Divide(this byte[] ip_byte_source, int ip_in_value)
        {
            throw new NotImplementedException();
        }
        /// <summary>
        /// Tính giai thừa của 1 số nguyên
        /// </summary>
        /// <param name="ip_int_value"></param>
        /// <returns></returns>
        public static byte[] GetFactorial(this int ip_int_value)
        {
            if (ip_int_value > 1)
                return GetFactorial(ip_int_value - 1).Mutiply(ip_int_value);
            else
                return new byte[1] { 1 };
        }
        /// <summary>
        /// Loại bỏ số 0 ở đầu vd: 004211456 -> 4211456
        /// </summary>
        /// <returns></returns>
        public static byte[] Trim(this byte[] c, int typeSize)
        {
            var lenc = c.GetLength(0);
            var flag = false;
            for (int i = lenc - 1; i >= 0; i--)
            {
                if (c[i] != 0)
                {
                    var newsize = (i + 1) % typeSize == 0 ? typeSize : ((i + 1) / typeSize + typeSize);
                    Array.Resize(ref c, newsize);
                    flag = true;
                    break;
                }
            }
            if (flag == false)
            {
                var newsize = (lenc) % typeSize == 0 ? typeSize : ((lenc) / typeSize + typeSize);
                Array.Resize(ref c, newsize);
            }
            return c;
        }

        public static byte[] Trim(this byte[] c)
        {
            var lenc = c.Length;
            var flag = false;
            for (int i = lenc - 1; i >= 0; i--)
            {
                if (c[i] != 0)
                {
                    var newSize = i + 1;
                    Array.Resize(ref c, newSize);
                    flag = true;
                    break;
                }
            }
            if (flag == false)
            {
                return new byte[1] { 0 };
            }
            return c;
        }

        public static bool IsZero(this byte[] c)
        {
            for (int i = 0; i < c.Length; i++)
            {
                if (c[i] != 0)
                {
                    return false;
                }
            }
            return true;
        }
        /// <summary>
        /// So sánh 2 mảng byte,
        /// Nếu mảng a > b trả về 1
        /// a = b trả về 0
        /// khác, b > a trả về -1
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        public static int Compare(this byte[] a, byte[] b)
        {
            var c1 = a.Trim();
            var c2 = b.Trim();
            if (c1.Length > c2.Length)
            {
                return 1;
            }
            else if (c1.Length < c2.Length)
            {
                return -1;
            }
            else // if ( a.Length == b.Length)
            {
                for (int i = c1.Length - 1; i >= 0; i--)
                {
                    if (c1[i] > c2[i])
                    {
                        return 1;
                    }
                    else if (c1[i] < c2[i])
                    {
                        return -1;
                    }
                }
            }
            return 0;
        }
        /// <summary>
        /// Chuyển đổi mảng giá trị sang hiển thị chuỗi
        /// </summary>
        /// <param name="ip_int_buffer">Mảng giá trị đầu vào</param>
        /// <param name="ip_int_base">Cơ số hiển thị</param>
        /// <returns></returns>
        public static string ToInt32String(this byte[] ip_int_buffer, int ip_int_base)
        {
            throw new NotImplementedException();
        }

        #endregion
    }
}
